﻿package com.designoutput.elements{
	import com.designoutput.event.ElementEvent;
	import com.designoutput.ui.editors.transformEditorClasses.TransformUtils;
	import com.designoutput.utils.MathUtils;
	import com.designoutput.utils.Unit;
	import com.designoutput.utils.UnitUtils;
	
	import flash.display.Shader;
	import flash.filters.ShaderFilter;
	import flash.geom.Matrix;
	import flash.geom.Point;
	import flash.utils.ByteArray;
	
	[Event(name="transformChanged", type="com.designoutput.event.ElementEvent")]
	public class BaseElement extends Base
	{
		private var _selected:Boolean;
		
		private var _scaleX:Number = 1;
		private var _scaleY:Number = 1;
		private var _skewX:Number = 0;
		private var _skewY:Number = 0;
		
		private var _useFill:Boolean; // fill color or not
		private var _fillColor:uint;	// background color
		private var _fillAlpha:Number;	// background alpha
		
		private var _useLine:Boolean;	// only line color or not
		private var _lineColor:uint; // line color
		private var _lineAlpha:Number; // line alpha
		private var _lineSize:Number; // line size
		
		private var _invalidElement:Boolean = true;
		
		[Embed("/pixelBender/SelectedShape.pbj", mimeType="application/octet-stream")]
		private var selectedFilter:Class;
		protected var allowSelectionFilter:Boolean = true;
		
		public function BaseElement()
		{
			mouseEnabled = false;
			_selected = false;
			
			_useLine = true;			
			_fillColor = 0xffffff;
			_fillAlpha = 1;
			
			_useFill = true;
			_lineColor = 0x000000;
			_lineAlpha = 1;
			_lineSize = 1;
		}
		
		public function getClassName():String
		{
			return "BaseElement";
		}
		
		public function get selected():Boolean
		{
			return _selected;
		}
		
		public function set selected(value:Boolean):void
		{
			_selected = value;
			invalidateDisplay();
		}
		
		override public function get scaleX():Number {
			return _scaleX;
		}
		
		override public function set scaleX(value:Number):void {
			_scaleX = value;
			dispatchEvent(new ElementEvent(ElementEvent.TRANSFORM_CHANGED));
			invalidateDisplay();
		}
		
		override public function get scaleY():Number {
			return _scaleY;
		}
		
		override public function set scaleY(value:Number):void {
			_scaleY = value;
			dispatchEvent(new ElementEvent(ElementEvent.TRANSFORM_CHANGED));
			invalidateDisplay();
		}
				
		public function get skewX():Number {
			return _skewX;
		}
		public function set skewX(value:Number):void {
			if(_skewX != value){
				_skewX = value;
				dispatchEvent(new ElementEvent(ElementEvent.TRANSFORM_CHANGED));
				invalidateDisplay();
			}
		}
		
		public function get skewY():Number {
			return _skewY;
		}
		public function set skewY(value:Number):void {
			if(_skewY != value){
				_skewY = value;
				dispatchEvent(new ElementEvent(ElementEvent.TRANSFORM_CHANGED));
				invalidateDisplay();
			}
		}
		
		override public function set width(value:Number):void {
			if(super.width != value){
				super.width = value;
				invalidateElement();
				dispatchEvent(new ElementEvent(ElementEvent.TRANSFORM_CHANGED));
			}
		}
		
		override public function set height(value:Number):void {
			if(super.height != value){
				super.height = value;
				invalidateElement();
				dispatchEvent(new ElementEvent(ElementEvent.TRANSFORM_CHANGED));
			}
		}
		
		override public function set rotation(value:Number):void {
			if(super.rotation != value){
				super.rotation = value;
				dispatchEvent(new ElementEvent(ElementEvent.TRANSFORM_CHANGED));
			}
		}
		
		public function get useFill():Boolean {
			return _useFill;
		}
		public function set useFill(value:Boolean):void
		{
			_useFill = value;
			invalidateElement();
		}
		
		public function get fillColor():uint{
			return _fillColor;
		}
		
		public function set fillColor(nColor:uint):void
		{
			_fillColor = nColor;
			invalidateElement();
		}

		public function get fillAlpha():Number
		{
			return _fillAlpha;
		}
		
		public function set fillAlpha(value:Number):void
		{
			_fillAlpha = value;
			invalidateElement();
		}
		
		public function get useLine():Boolean {
			return _useLine;
		}
		public function set useLine(value:Boolean):void
		{
			_useLine = value;
			invalidateElement();
		}
		
		public function get lineColor():uint
		{
			return _lineColor;
		}
		
		public function set lineColor(value:uint):void
		{
			_lineColor = value;
			invalidateElement();
		}
		
		public function get lineAlpha():Number
		{
			return _lineAlpha;
		}
		
		public function set lineAlpha(value:Number):void
		{
			_lineAlpha = value;
			invalidateElement();
		}
		
		public function get lineSize():Number
		{
			return _lineSize;
		}
		
		public function set lineSize(value:Number):void
		{
			_lineSize = value;
			invalidateElement();
		}
	
		protected function invalidateElement():void {
			_invalidElement = true;
			invalidateDisplay();
		}
		
		override protected function updateDisplay():void {
			super.updateDisplay();
			
			transform.matrix = getTransformMatrix();
			
			if(_selected) {
				var shader:Shader = new Shader(new selectedFilter() as ByteArray);
				var shaderFilter:ShaderFilter = new ShaderFilter(shader);
				
				if(allowSelectionFilter)
					filters = [ shaderFilter ];
			} else {
				filters = null;
			}
			
			if(_invalidElement){
				_invalidElement = false;
				updateElement();
			}
		}
		
		protected function updateElement():void {
		}
		
		protected function getTransformMatrix():Matrix {
			var m:Matrix = new Matrix();
			m.scale(scaleX, scaleY);
			if(skewX != 0 || skewY != 0)
			{
				var radianSkewX:Number = MathUtils.degreesToRadian(skewX);
				var radianSkewY:Number = MathUtils.degreesToRadian(skewY);
				
				m.a = Math.cos(radianSkewY);
				m.b = Math.sin(radianSkewY);
				m.c = -Math.sin(radianSkewX);
				m.d = Math.cos(radianSkewX);
			} else {
				m.rotate(MathUtils.degreesToRadian(rotation));
			}
			m.translate(x, y);
			return m;
		}
				
		public function getReferencePoints():Vector.<Point> {
			throw new Error("Not implemented.");
		}
		
		public function setReferencePoints(points:Vector.<Point>):void {
			throw new Error("Not implemented.");
		}
		
		public function toXML():XML
		{
			var elementXML:XML = <{getClassName()} />;
			
			elementXML.@x = UnitUtils.convert(x, Unit.PIXELS,  Unit.INCHES);
			elementXML.@y = UnitUtils.convert(y, Unit.PIXELS,  Unit.INCHES);
			
			elementXML.@w = UnitUtils.convert(width, Unit.PIXELS,  Unit.INCHES);
			elementXML.@h = UnitUtils.convert(height, Unit.PIXELS,  Unit.INCHES);
			
			elementXML.@rotation = rotation;
			elementXML.@skewX = skewX;
			elementXML.@skewY = skewY;
			
			elementXML.@useLine = String(_useLine);
			elementXML.@useFill = String(_useFill);
			elementXML.@lineSize = UnitUtils.convert(lineSize, Unit.PIXELS,  Unit.INCHES);
			elementXML.@lineColor = lineColor;
			elementXML.@bgColor = _fillColor;
			elementXML.@lineAlpha = lineAlpha;
			elementXML.@bgAlpha = fillAlpha;
			
			return elementXML;
		}
		
		public function fromXML(xmlNode:XML):void
		{
			x = UnitUtils.convert(Number(xmlNode.@x), Unit.INCHES,  Unit.PIXELS);
			y = UnitUtils.convert(Number(xmlNode.@y), Unit.INCHES,  Unit.PIXELS);
			
			width = UnitUtils.convert(Number(xmlNode.@w), Unit.INCHES,  Unit.PIXELS);
			height = UnitUtils.convert(Number(xmlNode.@h), Unit.INCHES,  Unit.PIXELS);
			
			rotation = Number(xmlNode.@rotation);
			skewX = Number(xmlNode.@skewX);
			skewY = Number(xmlNode.@skewY);
			
			_useLine = String(xmlNode.@useLine) != "false";
			_useFill = String(xmlNode.@useFill) != "false";
			lineSize = UnitUtils.convert(Number(xmlNode.@lineSize), Unit.INCHES,  Unit.PIXELS, 0);
			lineColor = uint(xmlNode.@lineColor);
			fillColor = uint(xmlNode.@bgColor);
			lineAlpha = Number(xmlNode.@lineAlpha == undefined ? 1 : xmlNode.@lineAlpha);
			fillAlpha = Number(xmlNode.@bgAlpha == undefined ? 1 : xmlNode.@bgAlpha);
		}
	}
}